// Package repl: repl Read Eval Print Loop (读取-求值-打印 循环) 可以认为是控制台或者交互模式，比如 cmd 输入 python 进入 python 的 repl
package repl

import (
	"bufio"
	"fmt"
	"io"
	"z-lang/cmd"
	"z-lang/compiler"
	"z-lang/lexer"
	"z-lang/object"

	//"z-lang/object"
	"z-lang/parser"
	"z-lang/vm"
)

const PROMPT = ">> "

func Start(in io.Reader, out io.Writer) {
	scanner := bufio.NewScanner(in)
	//env := object.NewEnvironment()

	constants := []object.Object{}
	globals := make([]object.Object, vm.GlobalSize)
	symbolTable := compiler.NewSymbolTable()
	for i, v := range object.Builtings {
		symbolTable.DefineBuiltin(i, v.Name)
	}

	for {
		_, _ = fmt.Fprintf(out, PROMPT)
		scanned := scanner.Scan()
		if !scanned {
			return
		}
		line := scanner.Text()

		// TODO 这里先这样，后续搞个内部命令处理函数
		if line == "EnableFuncTrace" {
			cmd.Cmd.FuncTrace = !cmd.Cmd.FuncTrace
			continue
		}
		if line == "quit" {
			break
		}
		// 词法分析
		lex := lexer.New(line)
		// 从输入的源代码中读取，直到读完一整行代码，将读取的代码行传递给词法分析器实例，然后输出词法分析器生成的词法单元，直到遇见EOF
		//for tok := lex.NextToken(); tok.Type != token.EOF; tok = lex.NextToken() {
		//	_, _ = fmt.Fprintf(out, "%+v\n", tok)
		//}

		// 语法分析
		p := parser.New(lex)
		program := p.ParserProgram()
		if len(p.Errors()) != 0 {
			printParserErrors(out, p.Errors())
			continue
		}

		// 求值
		//eval := evaluator.Eval(program, env)
		//if eval != nil && eval.Type() != object.EmptyObj {
		//	_, _ = io.WriteString(out, eval.Inspect())
		//	_, _ = io.WriteString(out, "\n")
		//}

		// 编译
		//comp := compiler.New()
		comp := compiler.NewWithState(symbolTable, constants)
		err := comp.Compile(program)
		if err != nil {
			_, _ = fmt.Fprintf(out, "Woops! Compilation failed: \n %s\n", err)
			continue
		}

		// 虚拟机
		//machine := vm.New(comp.Bytecode())
		machine := vm.NewWithGlobalStore(comp.Bytecode(), globals)
		err = machine.Run()
		if err != nil {
			_, _ = fmt.Fprintf(out, "Woops! Executing bytecode failed:\n %s\n", err)
			continue
		}

		// 虚拟机栈顶元素弹栈
		stackTop := machine.LastPoppedStackElem()
		_, _ = io.WriteString(out, stackTop.Inspect())
		_, _ = io.WriteString(out, "\n")
	}
}

func printParserErrors(out io.Writer, errors []string) {
	_, _ = io.WriteString(out, "Parser errors: \n")
	for _, msg := range errors {
		_, _ = io.WriteString(out, "\t"+msg+"\n")
	}
}
